#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define MYDEV_NAME "my_sample_chardev"
// 描述一个字符设备：应该有哪些属性：
// 1.设备名称，2.设备号：主设备好，次设备号，3.对设备进行操作的结构体struct file_operations

struct my_sample_chardev
{
    struct cdev *c_dev;
    struct class *mydev_class;
    struct device *mydev;
};

// 1.在全局中定义的 my_sample_charde的对象
struct my_sample_chardev my_chrdev;

char buff[10] = {0};

// 在内核定义对应的函数接口：
// 与文件read对应的函数指针
ssize_t my_sample_chardev_read(struct file *file, char __user *userbuf, size_t size, loff_t *offset)
{
    printk("内核中的my_sample_chardev_read执行了\n");
    copy_to_user(userbuf, buff + *offset, size);
    return size;
}
// 与write对应的函数指针
ssize_t my_sample_chardev_write(struct file *file, const char __user *userbuf, size_t size, loff_t *offset)
{
    printk("内核中的my_sample_chardev_write执行了\n");
    copy_from_user(buff + *offset, userbuf, size);
    return size;
}

// 与open对应的函数指针
int my_sample_chardev_open(struct inode *inode, struct file *file)
{
    printk("内核中的my_sample_chardev_open执行了\n");
    return 0;
}
// 与close对应函数指针：
int my_sample_chardev_release(struct inode *inode, struct file *file)
{
    printk("内核中的my_sample_chardev_release执行了\n");
    return 0;
}

struct file_operations fops = {
    .open = my_sample_chardev_open,
    .read = my_sample_chardev_read,
    .write = my_sample_chardev_write,
    .release = my_sample_chardev_release
};


// 入口函数：
int __init my_test_module_init(void)
{
    printk("A模块的入口函数执行了");

    my_chrdev.c_dev = cdev_alloc();
    if (my_chrdev.c_dev == NULL)
    {
        printk("cdev_alloc err:\n");
        return -ENXIO;
    }
    printk("111111111111111111111111\n");
    if (&fops == NULL)
    {
        printk("fops err:\n");
        return -ENXIO;
    }
    printk("2222222222222222222\n");
    cdev_init(my_chrdev.c_dev, &fops);
    printk("3333333333333333333\n");


    int ret = alloc_chrdev_region(&my_chrdev.c_dev->dev, 0, 1, MYDEV_NAME);
    if(ret)
    {
        printk("alloc_chrdev_region err\n");
        return ret;
    }
    printk("申请到的主设备号 = %d\n", MAJOR(my_chrdev.c_dev->dev));

    ret = cdev_add(my_chrdev.c_dev, my_chrdev.c_dev->dev, 1);
    if(ret)
    {
        printk("cdev_add err:");
        return ret;
    }
    printk("4444444444444\n");

    my_chrdev.mydev_class = class_create(MYDEV_NAME); 
    if(IS_ERR(my_chrdev.mydev_class))
    {
        printk("class_create失败\n");
        return PTR_ERR(my_chrdev.mydev_class);
    }
    printk("555555555555555\n");

    my_chrdev.mydev = device_create(my_chrdev.mydev_class, NULL, my_chrdev.c_dev->dev, NULL, MYDEV_NAME);
    if(IS_ERR(my_chrdev.mydev))
    {
        printk("device_create失败\n");
        return PTR_ERR(my_chrdev.mydev);
    }
    return 0;
}

// 出口函数：
void __exit my_test_module_exit(void)
{
    printk("出口函数执行了\n"); // 把调试信息放在了系统日志缓冲区，使用dmesg来显示。
    device_destroy(my_chrdev.mydev_class, my_chrdev.c_dev->dev);
    class_destroy(my_chrdev.mydev_class);
    cdev_del(my_chrdev.c_dev);
    unregister_chrdev_region(my_chrdev.c_dev, 1);
    kfree(my_chrdev.c_dev);
    //unregister_chrdev(my_chrdev.major,MYDEV_NAME);
}

// 指定许可：
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gaowanxi, email:gaonetcom@163.com");
// 指定入口及出口函数：
module_init(my_test_module_init);
module_exit(my_test_module_exit);